package com.kpf.springcloud.service.impl;

import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.kpf.springcloud.common.CommonUtil;
import com.kpf.springcloud.common.CreateOrderNo;
import com.kpf.springcloud.common.ResponseEnum;
import com.kpf.springcloud.config.ConfigUtil;
import com.kpf.springcloud.pojo.KpfPayOrder;
import com.kpf.springcloud.pojo.KpfUser;
import com.kpf.springcloud.service.IKpfPayOrderService;
import com.kpf.springcloud.service.IKpfUserService;
import com.kpf.springcloud.service.IWxPayService;
import com.kpf.springcloud.utils.GeneralUtil;
import com.kpf.springcloud.vo.PayResultVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.DecimalFormat;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author shiwen
 * @date 2021/3/9
 */
@Service
public class WxPayServiceImpl implements IWxPayService {

    private static final DecimalFormat df = new DecimalFormat("#");

    @Autowired
    private IKpfPayOrderService kpfPayOrderService;

    @Autowired
    private IKpfUserService userService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayResultVO createNative(String totalFee, String body, HttpServletRequest request, HttpServletResponse response) {
        String outTradeNo = CreateOrderNo.createOderNO();
        try {
            //todo 创建请求参数
            SortedMap<String, String> req = new TreeMap<String, String>();
            req.put("appid", ConfigUtil.APPID);    //公众号
            req.put("mch_id", ConfigUtil.MCH_ID);  // 商户号
            req.put("nonce_str", WXPayUtil.generateNonceStr()); // 32位随机字符串
            req.put("sub_mch_id", ConfigUtil.SUB_MCH_ID);
            req.put("body", "知识星球会员充值" + body + "积分"); // 商品描述, 知识星球会员充值10积分
            req.put("out_trade_no", outTradeNo);   // 商户订单号
            req.put("total_fee", df.format(Double.parseDouble(totalFee)));    // 标价金额(分)
            req.put("spbill_create_ip", CommonUtil.getIp(request));   // 终端IP
            req.put("notify_url", "http://www.baidu.com");  // 回调地址
            req.put("trade_type", "NATIVE");    // 交易类型
            req.put("sign", WXPayUtil.generateSignature(req, ConfigUtil.API_KEY, WXPayConstants.SignType.MD5));  // 签名

            //todo 生成要发送的 xml
            String xmlBody = WXPayUtil.generateSignedXml(req, ConfigUtil.API_KEY);
            System.err.println(String.format("微信支付预下单请求 xml 格式:\n%s", xmlBody));

            //todo 发送 POST 请求 统一下单 API 并携带 xmlBody 内容,然后获得返回接口结果
            String result = CommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", xmlBody);
            System.err.println(String.format("%s", result));

            //todo 将返回结果从 xml 格式转换为 map 格式
            Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
            PayResultVO payResultVO = PayResultVO.builder().codeUrl(resultMap.get("code_url")).outTradeNo(outTradeNo).totalFee(totalFee).build();
            // 生成订单交易记录
            kpfPayOrderService.saveKpfPayOrder(outTradeNo, Integer.parseInt(totalFee), Integer.parseInt(totalFee), 0);
            //Map<String, String> map = new HashMap<>();
            //map.put("code_url", ); // 支付地址
            //map.put("total_fee", ); // 总金额
            //map.put("out_trade_no", );    // 订单号
            return payResultVO;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 查询微信支付订单状态
     *
     * @param outTradeNo 订单号
     * @return 支付状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String queryOrder(String outTradeNo) {
        // 调用查询微信支付订单状态方法
        Map<String, String> map = this.queryPayStatus(outTradeNo);
        if (map.isEmpty()) {
            return ResponseEnum.FAIL.getMessage();
        }
        if (map.get("trade_state").equals("SUCCESS")) {
            // 生成二维码后, 只有当检测是否支付成功的时候才更新订单交易记录为已付款, 查询未付款的订单记录
            KpfPayOrder kpfPayOrder = kpfPayOrderService.getOne(kpfPayOrderService.getInitQueryWrapper().eq(KpfPayOrder::getStatus, 0)
                    .eq(KpfPayOrder::getOrderNo, outTradeNo));
            if (GeneralUtil.isObjNotNull(kpfPayOrder)) {
                // 更新为已付款状态
                kpfPayOrder.setStatus(1);
                kpfPayOrderService.updateById(kpfPayOrder);
                // 如果支付成功直接修改用户的类型为已支付用户
                // 用户类型 0：普通用户 1：付费用户 2：管理员
                KpfUser kpfUser = userService.getById(kpfPayOrder.getUserId());
                // 付费用户
                kpfUser.setUserType(1);
                // 更新用户类型为付费用户
                userService.updateById(kpfUser);
            }
            return "PAY_SUCCESS";
        } else if (map.get("trade_state").equals("NOTPAY")) {
            return "NOT_PAY";
        }
        return "FAIL";
    }

    public Map<String, String> queryPayStatus(String outTradeNo) {
        try {
            //todo 创建请求参数
            SortedMap<String, String> req = new TreeMap<String, String>();
            req.put("appid", ConfigUtil.APPID); // 公众号ID
            req.put("mch_id", ConfigUtil.MCH_ID);   // 商户号
            req.put("out_trade_no", outTradeNo);    // 订单号
            req.put("sub_mch_id", ConfigUtil.SUB_MCH_ID);
            req.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
            req.put("sign", WXPayUtil.generateSignature(req, ConfigUtil.API_KEY, WXPayConstants.SignType.MD5));

            //todo 生成要发送的 xml
            String xmlBody = WXPayUtil.generateSignedXml(req, ConfigUtil.API_KEY);
            //System.err.println(String.format("查询订单支付状态 xml 格式:\n%s", xmlBody));

            //todo 调用查询订单支付状态 API
            String result = CommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/orderquery", "POST", xmlBody);

            //todo 返回解析后的 map 数据
            Map<String, String> map = WXPayUtil.xmlToMap(result);
            //System.out.println(String.format("%s", map));
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}